home *** CD-ROM | disk | FTP | other *** search
- /* Comments.c: Comment handling for ProjectDrag
- *
- * A set of applets for drag and drop source control by Tim Maroney.
- * See develop, issue 23 for details.
- *
- * Built on DropShell by Leonard Rosenthol, Stephan Somogyi, and Marshall Clow,
- * and using the MoreFiles utilities by Jim Luther.
- *
- * This software is free, but don't modify and redistribute it without
- * changing the status window to indicate your name and your changes!
- */
-
-
- #include <Types.h>
- #include <Resources.h>
- #include <Dialogs.h>
- #include <ToolUtils.h>
-
- #include "PDUtilities.h"
- #include "PDDialogs.h"
- #include "Comments.h"
- #include "TasksAndErrors.h"
-
-
- #define kChangeCommentDialog 204
- #define kCommentItem 5
-
-
- typedef struct
- {
- StringPtr extension;
- StringPtr commentStart;
- StringPtr lineStart;
- StringPtr lineEnd;
- StringPtr blankLine;
- StringPtr commentEnd;
- } CommentFormat;
-
-
- /* XXX yeah, yeah, this table should be stored in a user-editable file -- maybe in 1.2 */
-
- #define kNumCommentFormats 12
-
- CommentFormat pCommentFormats[kNumCommentFormats] =
- {
- { "\p.c", "\p/*\n", "\p", "\p", "\p", "\p*/\n" },
- { "\p.h", "\p/*\n", "\p", "\p", "\p", "\p*/\n" },
- { "\p.cp", "\p/*\n", "\p", "\p", "\p", "\p*/\n" },
- { "\p.cc", "\p/*\n", "\p", "\p", "\p", "\p*/\n" },
- { "\p.cpp", "\p/*\n", "\p", "\p", "\p", "\p*/\n" },
- { "\p.pch", "\p/*\n", "\p", "\p", "\p", "\p*/\n" },
- { "\p.idl", "\p/*\n", "\p", "\p", "\p", "\p*/\n" },
- { "\p.r", "\p/*\n", "\p", "\p", "\p", "\p*/\n" },
- { "\p.p", "\p(*\n", "\p", "\p", "\p", "\p*)\n" },
- { "\p.a", "\p", "\p*", "\p", "\p*", "\p*\n" },
- { "\p.s", "\p", "\p*", "\p", "\p*", "\p*\n" },
- { "\p", "\p#\n", "\p#", "\p", "\p#", "\p#\n" } /* this line must be last */
- };
-
-
- pascal Boolean TheFilterProc(DialogPtr theDialog,EventRecord *ev,short *itemHit)
- {
- return StdFilterProc(theDialog, ev, itemHit);
- }
-
-
- void SavePreviousComment(StringPtr comment)
- {
- OSErr err;
- FSSpec file;
- Handle fileData = NULL;
- short refNum = -1;
-
- /* find the preferences folder */
- err = FindPreferencesFolder(&file.vRefNum, &file.parID);
- if (err != noErr) goto ErrorExit;
-
- /* set the Previous Comment file data */
- GetIndString(file.name, kProjectDragStrings, kPreviousCommentFileName);
- err = GetFileData(&file, &fileData, &refNum);
- if (err == fnfErr)
- {
- /* create and open the file */
- err = FSpCreate(&file, 'MPS ', 'TEXT', smSystemScript);
- if (err != noErr) goto ErrorExit;
- err = FSpOpenDF(&file, fsRdWrPerm, &refNum);
- if (err != noErr) goto ErrorExit;
- fileData = TempNewHandle(0, &err);
- }
- if (err != noErr) goto ErrorExit;
- SetHandleSize(fileData, 0);
- err = PtrAndHand(comment + 1, fileData, comment[0]);
- if (err != noErr) goto ErrorExit;
- err = WriteFileWithHeader(refNum, fileData, 0, NULL);
- if (err != noErr) goto ErrorExit;
-
- /* clean up and go */
- DisposeHandle(fileData);
- FSClose(refNum);
- return;
-
- ErrorExit:
- if (fileData != NULL)
- DisposeHandle(fileData);
- if (refNum != -1)
- FSClose(refNum);
- }
-
-
- void FetchPreviousComment(StringPtr comment)
- {
- OSErr err;
- FSSpec file;
- Handle fileData;
- short refNum;
-
- comment[0] = 0;
-
- /* find the preferences folder */
- err = FindPreferencesFolder(&file.vRefNum, &file.parID);
- if (err != noErr) return;
-
- /* get the Previous Comment file data */
- GetIndString(file.name, kProjectDragStrings, kPreviousCommentFileName);
- err = GetFileData(&file, &fileData, &refNum);
- if (err != noErr) return;
- FSClose(refNum);
- comment[0] = GetHandleSize(fileData);
- BlockMoveData(*fileData, comment + 1, comment[0]);
- DisposeHandle(fileData);
- }
-
-
- Boolean GetChangeComment(Boolean in, ConstStr255Param fileName, Str255 comment)
- {
- DialogPtr dialog;
- Boolean done = false;
- Boolean result = false;
- OSErr err = noErr;
- Str255 what;
- Str255 s;
- Rect r;
- Handle h;
- short type;
-
- GetIndString(s, kProjectDragStrings, kExplainChange);
- if (in)
- GetIndString(what, kProjectDragStrings, kWhatYouChangedIn);
- else
- GetIndString(what, kProjectDragStrings, kWhatYouWillDoWith);
- err = PtrToHand(s + 1, &h, s[0]);
- err = ReplaceString(h, "\p<1>", what);
- err = ReplaceString(h, "\p<2>", fileName);
- s[0] = GetHandleSize(h);
- BlockMoveData(*h, s + 1, s[0]);
- DisposeHandle(h);
- ParamText(s, NULL, NULL, NULL);
-
- dialog = GetNewDialog(kChangeCommentDialog, NULL, (WindowPtr)-1);
- if (dialog == NULL) return false;
- if (comment[0] == 0) FetchPreviousComment(comment);
- GetDialogItem(dialog, kCommentItem, &type, &h, &r);
- SetDialogItemText(h, comment);
- SelectDialogItemText(dialog, kCommentItem, 0, 32767);
- SetDialogDefaultItem(dialog, ok);
- SetDialogCancelItem(dialog, cancel);
- SetDialogTracksCursor(dialog, true);
- ShowWindow(dialog);
- while (!done)
- {
- short itemHit;
-
- ModalDialog(TheFilterProc, &itemHit);
- switch (itemHit)
- {
- case ok:
- GetDialogItem(dialog, kCommentItem, &type, &h, &r);
- GetDialogItemText(h, s);
- BlockMove(s, comment, s[0] + 1);
- SavePreviousComment(comment);
- done = true;
- result = true;
- break;
-
- case cancel:
- err = userCanceledErr;
- done = true;
- break;
- }
- }
- DisposeDialog(dialog);
- return result;
- }
-
-
- Boolean IsTextFile(FSSpec *file)
- {
- FInfo info;
- OSErr err;
-
- err = FSpGetFInfo(file, &info);
- if (err != noErr) return false;
- return info.fdType == 'TEXT';
- }
-
-
- CommentFormat *GetCommentFormat(FSSpec *file)
- {
- Str15 extension;
- long i, length;
-
- /* get the extension from the file name */
- for (i = file->name[0]; i > 0 && file->name[i] != '.'; i--)
- ;
- length = (file->name[0] - i) + 1;
- if (length > 5)
- return pCommentFormats + (kNumCommentFormats - 1);
- BlockMoveData(file->name + i, extension + 1, length);
- extension[0] = length;
-
- /* loop over the formats */
- for (i = 0; i < kNumCommentFormats; i++)
- {
- if (EqualString(extension, pCommentFormats[i].extension, false, true))
- return pCommentFormats + i;
- }
-
- /* return the default */
- return pCommentFormats + (kNumCommentFormats - 1);
- }
-
-
- OSErr SubstituteComments(StringHandle header, CommentFormat *format)
- {
- OSErr err;
- err = ReplaceString(header, "\p<comment start>", format->commentStart);
- if (err < noErr) return err;
- err = ReplaceString(header, "\p<line start>", format->lineStart);
- if (err < noErr) return err;
- err = ReplaceString(header, "\p<line end>", format->lineEnd);
- if (err < noErr) return err;
- err = ReplaceString(header, "\p<blank line>", format->blankLine);
- if (err < noErr) return err;
- if (err > noErr) err = noErr;
- return err;
- }
-
-
-
- OSErr GetHeaderFromFile(Handle fileData, CommentFormat *format,
- StringHandle blankHeader, StringHandle *header)
- {
- OSErr err;
- Byte headerState, fileState;
- StringPtr fileLine;
- StringPtr headerLine;
- Boolean lineDoesntMatch;
- Boolean neverMatched;
- Boolean lastLineMatchedCommentEnd;
- long headerLineLength, headerLength, fileLineLength, fileLength;
-
- /* loop over lines to match each line of blank header,
- * accumulating lines into the header string handle
- */
- *header = NULL;
- headerState = HGetState(blankHeader);
- HLock(blankHeader);
- fileState = HGetState(fileData);
- HLock(fileData);
- fileLength = GetHandleSize(fileData);
- headerLength = GetHandleSize(blankHeader);
- headerLine = *blankHeader;
- fileLine = *fileData;
- headerLineLength = LineSize(headerLine, headerLength);
- fileLineLength = LineSize(fileLine, fileLength);
- lineDoesntMatch = true;
- neverMatched = true;
- lastLineMatchedCommentEnd = false;
- do
- {
- /* check for end of comment -- it means failure when followed by a blank line */
- if (lastLineMatchedCommentEnd && fileLineLength == 1 && *fileLine == '\n')
- {
- err = -1;
- break;
- }
-
- lastLineMatchedCommentEnd = MatchLine(fileLine, fileLineLength, format->commentEnd + 1, format->commentEnd[0]);
-
- if (MatchLineUntilChar(fileLine, fileLineLength, headerLine, headerLineLength, '<'))
- {
- lineDoesntMatch = neverMatched = false;
-
- /* start matching file to next header line */
- headerLine += headerLineLength;
- headerLength -= headerLineLength;
- headerLineLength = LineSize(headerLine, headerLength);
-
- /* skip blank lines in header */
- while (headerLength > 0
- && MatchLine(headerLine, headerLineLength, format->lineStart + 1,
- format->lineStart[0]))
- {
- headerLine += headerLineLength;
- headerLength -= headerLineLength;
- headerLineLength = LineSize(headerLine, headerLength);
- }
- }
- else
- {
- lineDoesntMatch = true;
-
- /* it's OK if a line doesn't match as long as we have been matching
- * up to this point, because extra lines of comment are OK.
- */
- if (neverMatched) break;
- }
-
- /* accumulate line into header handle */
- if (*header == NULL)
- {
- *header = TempNewHandle(0, &err);
- if (err != noErr) break;
- }
- err = PtrAndHand(fileLine, *header, fileLineLength);
-
- /* advance the file data pointer */
- fileLine += fileLineLength;
- fileLength -= fileLineLength;
- fileLineLength = LineSize(fileLine, fileLength);
-
- } while (err == noErr && headerLength > 0 && fileLength > 0);
-
- HSetState(fileData, fileState);
- HSetState(blankHeader, headerState);
-
- if (lineDoesntMatch || err != noErr)
- {
- /* XXX put up a "header corrupt" warning when something matched */
- if (*header != NULL)
- {
- DisposeHandle(*header);
- *header = NULL;
- }
- }
-
- return (lineDoesntMatch && err == noErr) ? -1 : err;
- }
-
-
- OSErr PadToColumn(Handle h, long column)
- {
- OSErr err;
- long lineStart, i;
- long sizeNow = GetHandleSize(h), newSize;
-
- /* track backwards looking for end of line */
- for (lineStart = sizeNow; lineStart > 0 && (*h)[lineStart-1] != '\n'; lineStart--)
- ;
-
- if (sizeNow - lineStart >= column)
- {
- /* already gone too far -- insert a single space */
- SetHandleSize(h, sizeNow + 1);
- err = MemError();
- if (err != noErr) return err;
- (*h)[sizeNow] = ' ';
- return noErr;
- }
-
- /* add spaces until we get to the column */
- newSize = sizeNow + (column - (sizeNow - lineStart));
- SetHandleSize(h, newSize);
- err = MemError();
- if (err != noErr) return err;
- for (i = sizeNow; i < newSize; i++)
- (*h)[i] = ' ';
- return noErr;
- }
-
-
- Boolean IsChangeCommentStartLine(StringPtr headerLine, long length,
- CommentFormat *format)
- {
- StringPtr s = headerLine;
-
- /* match the start of line sequence, if not null */
- if (format->lineStart != NULL && *format->lineStart != 0)
- {
- long len2 = format->lineStart[0];
- StringPtr s2 = format->lineStart + 1;
- while (len2-- > 0 && length-- > 0)
- {
- if (*s2++ != *s++)
- return false;
- }
- }
-
- /* skip any number of tabs and spaces */
- while (*s == '\t' || *s == ' ') s++;
-
- /* found it if the first non-tab is an angle bracket */
- return (*s == '<');
- }
-
-
- OSErr FindFirstChangeComment(StringHandle header, CommentFormat *format,
- long *start, long *end)
- {
- StringPtr headerLine;
- Byte state;
- long headerLineLength;
- long headerLength;
- Boolean found = false;
- Boolean lastLineMatchedCommentEnd;
-
- state = HGetState(header);
- HLock(header);
- headerLine = *header;
- headerLength = GetHandleSize(header);
- headerLineLength = LineSize(headerLine, headerLength);
- lastLineMatchedCommentEnd = false;
-
- /* loop over lines to find first line starting with tabs and angle bracket */
- do
- {
- /* check for end of comment */
- if (lastLineMatchedCommentEnd && headerLineLength == 1 && *headerLine == '\n')
- {
- /* no change comment */
- *start = *end = (headerLine - (*header)) - 1;
- break;
- }
-
- lastLineMatchedCommentEnd = MatchLine(headerLine, headerLineLength, format->commentEnd + 1, format->commentEnd[0]);
-
- found = IsChangeCommentStartLine(headerLine, headerLineLength, format);
-
- if (!found)
- {
- /* if not found, advance to next line */
- headerLine += headerLineLength;
- headerLength -= headerLineLength;
- headerLineLength = LineSize(headerLine, headerLength);
- }
- else
- {
- /* if found, record the start of the comment and scan for the next or the end */
- *start = headerLine - *header;
-
- headerLine += headerLineLength;
- headerLength -= headerLineLength;
- headerLineLength = LineSize(headerLine, headerLength);
- lastLineMatchedCommentEnd = false;
- do
- {
- if (MatchLine(headerLine, headerLineLength, format->commentEnd + 1, format->commentEnd[0]))
- break; /* adding the first change comment */
-
- if (IsChangeCommentStartLine(headerLine, headerLineLength, format))
- break; /* found start of next change comment */
-
- /* try the next line */
- headerLine += headerLineLength;
- headerLength -= headerLineLength;
- headerLineLength = LineSize(headerLine, headerLength);
- } while (headerLength > 0);
-
- *end = (headerLine - (*header)) - 1;
- }
- } while (headerLength > 0 && !found);
-
- HSetState(header, state);
-
- return noErr;
- }
-
-
- OSErr GetRevisionNumber(FSSpec *file, short *revision)
- {
- CKIDHandle theCKID;
- OSErr err;
-
- /* get the CKID */
- err = ExtractCKID(file, &theCKID);
-
- if (err != noErr)
- {
- /* if no CKID, it's revision 0! */
- *revision = 0;
- }
- else
- {
- /* otherwise it's the number in the CKID */
- Str31 revisionString;
- long longRevision;
- StringPtr s = (*theCKID)->projectPath; /* careful -- not locked down */
- s += s[0] + 2; /* skip the project name and null character */
- s += s[0] + 2; /* skip the user name and null character */
- BlockMoveData(s, revisionString, s[0] + 1); /* copy the revision number */
- StringToNum(revisionString, &longRevision);
- *revision = (short)longRevision;
- DisposeHandle((Handle)theCKID);
- }
- return noErr;
- }
-
-
- OSErr BuildComment(Handle *commentHandle, CommentFormat *format, short revision,
- Boolean checkingOut, StringPtr comment, StringPtr nickname)
- {
- OSErr err;
- Str31 revisionString;
- unsigned long sects;
- Str31 dateString;
- char c;
-
- /* Build the change comment, aligned to four-column format.
- * The end of the revision number (">") falls on column twelve (12);
- * the end of the date on column twenty-four (24);
- * the start of the initials on column twenty-nine (29);
- * the start of the comment on column thirty-seven (37).
- */
- err = PtrToHand(format->lineStart + 1, commentHandle, format->lineStart[0]);
- if (err != noErr) goto ErrorExit;
-
- /* add the revision string */
- NumToString(checkingOut ? revision : revision + 1, revisionString);
- if (checkingOut) revisionString[++revisionString[0]] = '+';
- err = PadToColumn(*commentHandle, 12 - (revisionString[0] + 2));
- if (err != noErr) goto ErrorExit;
- c = '<';
- err = PtrAndHand(&c, *commentHandle, 1);
- if (err != noErr) goto ErrorExit;
- err = PtrAndHand(revisionString + 1, *commentHandle, revisionString[0]);
- if (err != noErr) goto ErrorExit;
- c = '>';
- err = PtrAndHand(&c, *commentHandle, 1);
- if (err != noErr) goto ErrorExit;
-
- /* add the date string */
- GetDateTime(§s);
- DateString(sects, shortDate, dateString, NULL);
- err = PadToColumn(*commentHandle, 24 - dateString[0]);
- if (err != noErr) goto ErrorExit;
- err = PtrAndHand(dateString + 1, *commentHandle, dateString[0]);
- if (err != noErr) goto ErrorExit;
-
- /* add the nickname */
- err = PadToColumn(*commentHandle, 28);
- if (err != noErr) goto ErrorExit;
- err = PtrAndHand(nickname + 1, *commentHandle, nickname[0]);
- if (err != noErr) goto ErrorExit;
-
- /* add the comment, splitting among multiple lines if necessary;
- * max line length is 100, so the max size of a section is 100-37 = 63
- */
- err = PadToColumn(*commentHandle, 36);
- if (err != noErr) goto ErrorExit;
- if (comment[0] <= 63)
- {
- err = PtrAndHand(comment + 1, *commentHandle, comment[0]);
- if (err != noErr) goto ErrorExit;
- }
- else
- {
- /* split that sucker */
- StringPtr strStart = comment + 1;
- long strLen = comment[0];
- while (strLen > 0)
- {
- StringPtr strEnd;
- if (strLen > 63)
- {
- strEnd = strStart + 63;
- while (strEnd[0] != ' ' && strEnd[0] != '\t' && strEnd > strStart)
- strEnd--;
- if (strStart == strEnd)
- {
- /* there were no spaces -- split arbitrarily at 63... */
- strEnd = strStart + 63;
- }
- }
- else
- {
- strEnd = strStart + strLen;
- }
-
- /* add this chunk */
- err = PtrAndHand(strStart, *commentHandle, strEnd - strStart);
- if (err != noErr) goto ErrorExit;
-
- /* advance to next chunk */
- strLen -= (strEnd - strStart);
- strStart = strEnd;
- while (strLen > 0 && (strStart[0] == ' ' || strStart[0] == '\t'))
- strStart++, strLen--;
-
- /* add the end of this line and the start of the next */
- if (strLen > 0)
- {
- err = PtrAndHand(format->lineEnd + 1, *commentHandle, format->lineEnd[0]);
- if (err != noErr) goto ErrorExit;
- c = '\n';
- err = PtrAndHand(&c, *commentHandle, 1);
- if (err != noErr) goto ErrorExit;
- err = PtrAndHand(format->lineStart + 1, *commentHandle, format->lineStart[0]);
- if (err != noErr) goto ErrorExit;
- err = PadToColumn(*commentHandle, 36);
- if (err != noErr) goto ErrorExit;
- }
- }
- }
-
- /* add the end of line */
- err = PtrAndHand(format->lineEnd + 1, *commentHandle, format->lineEnd[0]);
- if (err != noErr) goto ErrorExit;
- c = '\n';
- err = PtrAndHand(&c, *commentHandle, 1);
- if (err != noErr) goto ErrorExit;
- return noErr;
-
- ErrorExit:
- if (*commentHandle != NULL)
- {
- DisposeHandle(*commentHandle);
- *commentHandle = NULL;
- }
- return err;
- }
-
-
- OSErr GetFirstTimeHeader(StringHandle *header, FSSpec *file, CommentFormat *format,
- StringPtr userName, StringPtr nickname, StringPtr comment, Boolean checkingOut)
- {
- StringHandle commentHandle = NULL;
- short revision;
- OSErr err = noErr;
- char c;
-
- /* get the template from the string in our resource file */
- *header = Get1Resource('Comm', 1);
- if (*header == NULL)
- {
- err = resNotFound;
- goto ErrorExit;
- }
- DetachResource(*header);
-
- /* substitute the comment characters */
- err = SubstituteComments(*header, format);
- if (err < noErr) goto ErrorExit;
-
- /* substitute the file name */
- err = ReplaceString(*header, "\p<file name>", file->name);
- if (err < noErr) goto ErrorExit;
-
- /* substitute the user name */
- err = ReplaceString(*header, "\p<user name>", userName);
- if (err < noErr) goto ErrorExit;
-
- /* XXX substitute the copyright */
-
- /* get the revision number from the CKID */
- err = GetRevisionNumber(file, &revision);
- if (err != noErr) goto ErrorExit;
-
- err = BuildComment(&commentHandle, format, revision, checkingOut, comment, nickname);
- if (err != noErr) goto ErrorExit;
-
- /* append the change comment to the header */
- err = HandAndHand(commentHandle, *header);
- DisposeHandle(commentHandle);
- commentHandle = NULL;
- if (err != noErr) goto ErrorExit;
-
- /* append the comment end */
- err = PtrAndHand(format->commentEnd + 1, *header, format->commentEnd[0]);
- if (err != noErr) goto ErrorExit;
-
- /* add another blank line or two */
- c = '\n';
- err = PtrAndHand(&c, *header, 1);
- if (err != noErr) goto ErrorExit;
- err = PtrAndHand(&c, *header, 1);
- if (err != noErr) goto ErrorExit;
-
- return noErr;
-
- ErrorExit:
- if (*header != NULL)
- {
- DisposeHandle(*header);
- *header = NULL;
- }
- if (commentHandle != NULL)
- DisposeHandle(commentHandle);
- return err;
- }
-
-
- OSErr ExtractFileHeader(CommentFormat *format, Handle fileData,
- StringHandle *header, long *headerEnd)
- {
- StringHandle blankHeader = NULL;
- Boolean atEnd;
- Boolean lastLineMatchedCommentEnd;
- OSErr err;
- StringPtr fileLine;
- long fileLength, fileLineLength, headerLength;
- Byte fileState;
-
- /* get blank header from string resource */
- blankHeader = Get1Resource('Comm', 1);
- if (blankHeader == NULL)
- {
- err = resNotFound;
- goto ErrorExit;
- }
- DetachResource(blankHeader);
-
- /* substitute the comment characters */
- err = SubstituteComments(blankHeader, format);
- if (err != noErr) goto ErrorExit;
-
- /* get the header */
- err = GetHeaderFromFile(fileData, format, blankHeader, header);
- if (err != noErr) goto ErrorExit;
-
- /* matched it! loop over lines to the end of comment, adding each
- * line to the handle
- */
- headerLength = GetHandleSize(*header);
- fileState = HGetState(fileData);
- HLock(fileData);
- fileLength = GetHandleSize(fileData) - headerLength;
- fileLine = (*fileData) + headerLength;
- fileLineLength = LineSize(fileLine, fileLength);
- atEnd = lastLineMatchedCommentEnd = false;
- do
- {
- atEnd = lastLineMatchedCommentEnd && fileLineLength == 1 && *fileLine == '\n';
- if (atEnd) break;
-
- lastLineMatchedCommentEnd = MatchLine(fileLine, fileLineLength, format->commentEnd + 1, format->commentEnd[0]);
-
- /* accumulate line into header */
- err = PtrAndHand(fileLine, *header, fileLineLength);
- if (err != noErr) goto ErrorExit;
-
- /* advance the file data pointer */
- fileLine += fileLineLength;
- fileLength -= fileLineLength;
- fileLineLength = LineSize(fileLine, fileLength);
- } while (fileLength > 0);
-
- HSetState(fileData, fileState);
-
- /* return the end-of-header offset */
- *headerEnd = GetHandleSize(*header);
-
- DisposeHandle(blankHeader);
- return noErr;
-
- ErrorExit:
- if (blankHeader != NULL)
- DisposeHandle(blankHeader);
- return err;
- }
-
-
- OSErr ExtractCurrentChangeComment(CommentFormat *format, StringHandle header, StringPtr comment)
- {
- OSErr err = noErr;
- StringHandle h = NULL;
- long start, end;
- Byte state;
- StringPtr s;
- long i;
- long stripLength;
- long length;
- Boolean gotPlusSign;
-
- /* get the location of the first change comment */
- err = FindFirstChangeComment(header, format, &start, &end);
- if (err != noErr) return err;
- length = end - start;
-
- /* extract the change comment */
- h = TempNewHandle(end - start, &err);
- if (err != noErr) return err;
- BlockMoveData((*header) + start, *h, length);
-
- /* strip the revision number, initials, and date */
- state = HGetState(h);
- HLock(h);
- gotPlusSign = false;
- for (i = 0, s = *h; i < length && *s != '>'; i++, s++)
- if (*s == '+') gotPlusSign = true;
- /* strip to end of revision number -- right angle bracket */
- if (!gotPlusSign)
- {
- comment[0] = 0;
- DisposeHandle(h);
- return noErr;
- }
- for (s++; i < length && (*s == '\t' || *s == ' '); i++, s++)
- ; /* strip tabs and spaces */
- for ( ; i < length && (*s != '\t') && (*s != ' '); i++, s++)
- ; /* strip non-white-space (date) */
- for ( ; i < length && (*s == '\t' || *s == ' '); i++, s++)
- ; /* strip tabs and spaces */
- for ( ; i < length && (*s != '\t') && (*s != ' '); i++, s++)
- ; /* strip non-white-space (initials) */
- for ( ; i < length && (*s == '\t' || *s == ' '); i++, s++)
- ; /* strip tabs and spaces */
- stripLength = (s - *h);
- HSetState(h, state);
- if (length > stripLength)
- {
- length -= stripLength;
- BlockMoveData(*h + stripLength, *h, length);
- SetHandleSize(h, length);
- }
- else
- {
- SetHandleSize(h, 0);
- length = 0;
- }
-
- /* strip start of line characters */
- if (format->lineStart[0] > 0)
- {
- Str31 theLineStart;
- theLineStart[0] = format->lineStart[0] + 1;
- theLineStart[1] = '\n';
- BlockMoveData(format->lineStart + 1, theLineStart + 2, format->lineStart[0]);
- while (ReplaceString(h, theLineStart, "\p ") > 0)
- ;
- }
-
- /* compress white space */
- ReplaceString(h, "\p\n", "\p ");
- ReplaceString(h, "\p\t", "\p ");
- while (ReplaceString(h, "\p ", "\p ") > 0)
- ;
- length = GetHandleSize(h);
-
- /* copy to the comment string */
- BlockMoveData(*h, comment + 1, length);
- comment[0] = length;
- DisposeHandle(h);
- return noErr;
- }
-
-
- OSErr AddChangeComment(FSSpec *file, CommentFormat *format, StringHandle header,
- StringPtr nickname, StringPtr comment, Boolean checkingOut)
- {
- OSErr err = noErr;
- long start, end;
- short revision;
- StringHandle commentHandle = NULL;
- Byte state;
- long newCommentSize;
-
- /* get the location of the first change comment */
- err = FindFirstChangeComment(header, format, &start, &end);
- if (err != noErr) goto ErrorExit;
-
- /* get the revision number from the CKID */
- err = GetRevisionNumber(file, &revision);
- if (err != noErr) goto ErrorExit;
-
- err = BuildComment(&commentHandle, format, revision, checkingOut, comment,
- nickname);
- if (err != noErr) goto ErrorExit;
-
- /* check the current comment -- is it for this revision with a plus sign?
- * if so, delete it. this is only when checking in...
- */
- state = HGetState(header);
- HLock(header);
- HLock(commentHandle); /* permanent lock -- disposed a little later */
- newCommentSize = GetHandleSize(commentHandle);
-
- if (!checkingOut)
- {
- /* check to see if the current comment is for this revision */
- StringPtr s;
- long i;
-
- for (i = 0, s = (*header) + start; i < end - start && *s != '<'; i++, s++)
- ;
- if (i < end - start && *s == '<')
- {
- Str31 numberString;
- long theNumber;
-
- numberString[0] = 0;
- for (s++, i++; i < end - start && *s >= '0' && *s <= '9'; i++, s++)
- numberString[++numberString[0]] = *s;
- StringToNum(numberString, &theNumber);
- if (theNumber == revision && *s == '+' && i < end - start)
- {
- /* delete the current comment */
- Munger(header, start, NULL, (end - start) + 1, *commentHandle, 0);
- }
- }
- }
-
- /* now add the comment at offset "start" */
- HUnlock(header); /* needs to be unlocked for Munger! */
- Munger(header, start, NULL, 0, *commentHandle, newCommentSize);
-
- /* clean up and go away */
- HSetState(header, state);
- DisposeHandle(commentHandle);
- return noErr;
-
- ErrorExit:
- if (commentHandle != NULL)
- DisposeHandle(commentHandle);
- return err;
- }
-
-
- OSErr AddFirstTimeHeader(FSSpec *file, StringPtr userName, StringPtr nickname, StringPtr comment)
- {
- StringHandle header = NULL;
- CommentFormat *format = NULL;
- Handle fileData;
- short refNum;
- OSErr err;
-
- /* is it a text file? if not, nothing to do */
- if (!IsTextFile(file))
- return noErr;
-
- /* does the user confirm that a header should be added? */
- switch (ResTextYesNoCancel(kProjectDragStrings, kAddHeader, file->name, NULL, NULL, NULL))
- {
- case kConfirmYes:
- break;
- case kConfirmNo:
- return noErr;
- case kConfirmCancel:
- return userCanceledErr;
- }
-
- TaskStart(kProjectDragStrings, kAddingChangeComment, file->name, NULL, NULL, NULL);
-
- /* find the comment information for this file type */
- format = GetCommentFormat(file);
-
- /* get new header with first time change comment */
- err = GetFirstTimeHeader(&header, file, format, userName, nickname, comment, false);
- if (err != noErr) return RaiseErrorNumber(err);
-
- /* get the original file data */
- err = GetFileData(file, &fileData, &refNum);
- if (err != noErr) return RaiseErrorNumber(err);
-
- /* write the file with its header */
- err = WriteFileWithHeader(refNum, fileData, 0, header);
- FSClose(refNum);
- DisposeHandle(header);
- DisposeHandle(fileData);
- if (err == noErr)
- TaskDone();
- else
- RaiseErrorNumber(err);
- return err;
- }
-
-
- OSErr AddCheckinComment(FSSpec *file, StringPtr userName, StringPtr nickname, StringPtr comment)
- {
- StringHandle header = NULL;
- CommentFormat *format = NULL;
- long headerEnd = 0;
- short refNum = -1;
- Handle fileData = NULL;
- OSErr err;
-
- /* is it a text file? if not, just get an internal comment and get out - no text munging */
- if (!IsTextFile(file))
- {
- if (!GetChangeComment(true, file->name, comment))
- return userCanceledErr;
- else
- return noErr;
- }
-
- TaskStart(kProjectDragStrings, kAddingChangeComment, file->name, NULL, NULL, NULL);
-
- /* find the comment information for this file type */
- format = GetCommentFormat(file);
-
- /* get the original file data */
- err = GetFileData(file, &fileData, &refNum);
- if (err != noErr) goto ErrorExit;
-
- /* does the file have a header comment? */
- err = ExtractFileHeader(format, fileData, &header, &headerEnd);
- if (err != noErr)
- {
- /* no header -- does the user confirm adding one? */
- switch (ResTextYesNoCancel(kProjectDragStrings, kAddHeader, file->name, NULL, NULL, NULL))
- {
- case kConfirmYes:
- break;
- case kConfirmNo:
- FSClose(refNum);
- TaskDone();
- return noErr;
- case kConfirmCancel:
- err = userCanceledErr;
- goto ErrorExit;
- }
-
- /* user confirmed -- get comment from user */
- if (!GetChangeComment(true, file->name, comment))
- {
- err = userCanceledErr;
- goto ErrorExit;
- }
-
- /* get new header with first time change comment */
- err = GetFirstTimeHeader(&header, file, format, userName, nickname, comment, false);
- if (err != noErr) goto ErrorExit;
- }
- else
- {
- /* extract the change comment from the header */
- err = ExtractCurrentChangeComment(format, header, comment);
- if (err != noErr) goto ErrorExit;
-
- /* get the change comment from the user */
- if (!GetChangeComment(true, file->name, comment))
- {
- err = userCanceledErr;
- goto ErrorExit;
- }
-
- /* put the change comment into the header */
- err = AddChangeComment(file, format, header, nickname, comment, false);
- if (err != noErr) goto ErrorExit;
- }
-
- /* write the file with its header */
- err = WriteFileWithHeader(refNum, fileData, headerEnd, header);
- if (err != noErr) goto ErrorExit;
- FSClose(refNum);
- DisposeHandle(header);
- DisposeHandle(fileData);
- TaskDone();
- return noErr;
-
- ErrorExit:
- if (refNum != -1)
- FSClose(refNum);
- if (header != NULL)
- DisposeHandle(header);
- if (fileData != NULL)
- DisposeHandle(fileData);
- return RaiseErrorNumber(err);
- }
-
-
- OSErr AddCheckoutComment(FSSpec *file, StringPtr userName, StringPtr nickname, StringPtr comment)
- {
- StringHandle header = NULL;
- CommentFormat *format = NULL;
- long headerEnd = 0;
- short refNum = -1;
- Handle fileData = NULL;
- OSErr err;
-
- /* is it a text file? if not, nothing to do */
- if (!IsTextFile(file)) return noErr;
-
- TaskStart(kProjectDragStrings, kAddingChangeComment, file->name, NULL, NULL, NULL);
-
- /* find the comment information for this file type */
- format = GetCommentFormat(file);
-
- /* get the original file data */
- err = GetFileData(file, &fileData, &refNum);
- if (err != noErr) goto ErrorExit;
-
- /* does the file have a header comment? */
- err = ExtractFileHeader(format, fileData, &header, &headerEnd);
- if (err != noErr)
- {
- /* no header -- does the user confirm adding one? */
- switch (ResTextYesNoCancel(kProjectDragStrings, kAddHeader, file->name, NULL, NULL, NULL))
- {
- case kConfirmYes:
- break;
- case kConfirmNo:
- FSClose(refNum);
- TaskDone();
- return noErr;
- case kConfirmCancel:
- err = userCanceledErr;
- goto ErrorExit;
- }
-
- /* get new header with first time change comment */
- err = GetFirstTimeHeader(&header, file, format, userName, nickname, comment, true);
- if (err != noErr) goto ErrorExit;
- }
- else
- {
- /* has header -- add change comment */
- err = AddChangeComment(file, format, header, nickname, comment, true);
- if (err != noErr) goto ErrorExit;
- }
-
- /* write the file with its header */
- err = WriteFileWithHeader(refNum, fileData, headerEnd, header);
- if (err != noErr) goto ErrorExit;
- FSClose(refNum);
- DisposeHandle(header);
- DisposeHandle(fileData);
- TaskDone();
- return noErr;
-
- ErrorExit:
- if (refNum != -1)
- FSClose(refNum);
- if (header != NULL)
- DisposeHandle(header);
- if (fileData != NULL)
- DisposeHandle(fileData);
- return RaiseErrorNumber(err);
- }
-